﻿using System;
using System.IO;
using System.Net;
using System.Reflection;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
//using System.Windows.Media.Animation;
using System.Windows.Controls.Primitives;
using System.Windows.Navigation;

using Heaven.Graphics;
using Heaven.Geometry.Procedural;
using Heaven.SlimDX.Graphics;
using Heaven.Geometry;
using Heaven.Mathematics;
using Heaven.Engine.Animation;

using Heaven.Engine;
using Color = Heaven.Graphics.Color;
using CameraBrowser = Heaven.Engine.CameraBrowser;
using Wavefront = Heaven.Geometry.Importers.Wavefront;
using Visual = Heaven.Engine.Visual;
using Heaven.Engine.Transformations;
using HeavenAnimation = Heaven.Engine.Animation;

namespace Heaven.Showcase.Demos
{
    /// <summary>
    /// Demonstrations. Interactive demo
    /// </summary>
	public partial class Balls
    {
        #region Ball's mesh

        static Polygonal ballMesh = null;
        static Polygonal BallMesh
        {
            get
            {
                if (ballMesh == null)
                {
                    Sphere sphere = new Sphere(Vector3.Zero, 1.0);
                    ballMesh = sphere.Generate();
                }
                return ballMesh;
            }
        }

        static Polygonal GetRandomBallMesh()
        {
            Random random = new Random();
            Sphere sphere = new Sphere(Vector3.Zero, random.NextDouble() * 3.0 + 0.2);
            sphere.Quality = 0.5;
            return sphere.Generate();
        }

        #endregion

        #region Fields

        // Heaven device compatible with 
        // Windows Presentation Foundation
        WindowsPresentationDevice device = null;
        CoreRenderer render = null;

        // Scene, as a container for objects
        NodeGroup nodeGroup = new NodeGroup();

        // Camera with particular behaviour
        CameraBrowser camera = new CameraBrowser();
        // It's for dragging
        double mouseX = Double.NaN;
        double mouseY = Double.NaN;
        
        // Storyboard for animation timelines
        Storyboard storyboard = new Storyboard();

        // Scale animations
        Dictionary<Vector3, Timeline> scaleTimelines = new Dictionary<Vector3, Timeline>();
        // Color animations
        Dictionary<Vector3, Timeline> colorTimelines = new Dictionary<Vector3, Timeline>();


        DispatcherTimer timerAnimation = null;
        DispatcherTimer timerAddition = null;

        DefaultTechnique defaultTechnique = new DefaultTechnique();

        #endregion

        #region Initializations

        /// <summary>
        /// Constructor
        /// </summary>
        public Balls()
		{
			this.InitializeComponent();
            this.Loaded += new RoutedEventHandler(OnLoaded);
            //this.MouseLeftButtonUp += new MouseButtonEventHandler(OnMouseLeftButtonUp);

            timerAnimation = new DispatcherTimer();
            timerAnimation.Interval = TimeSpan.FromSeconds(1);
            timerAnimation.Tick += new EventHandler(OnMouseLeftButtonUp);
            timerAnimation.Start();

            timerAddition = new DispatcherTimer();
            timerAddition.Interval = TimeSpan.FromSeconds(0.001);
            timerAddition.Tick += new EventHandler(OnMouseDoubleClick);
            timerAddition.Start();

            Focusable = true;
            MouseDoubleClick += delegate { device.Invalidate(); };
        }

        void OnMouseDown(object sender, MouseButtonEventArgs e)
        {
            Focus();
        }

        void OnKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Space)
            {
                if (timerAddition.IsEnabled)
                {
                    timerAddition.Stop();
                    timerAnimation.Stop();
                    nodeGroup.Clear();
                    storyboard.Clear();
                    scaleTimelines.Clear();
                    colorTimelines.Clear();
                }
                else
                {
                    timerAnimation.Start();
                    timerAddition.Start();
                }
            }
        }
             
        #endregion

        #region Manipulation routines
               

        void OnMouseLeftButtonUp(object sender, EventArgs e)
        {
            /*foreach (Timeline timeline in scaleTimelines.Values)
            {
                storyboard.Begin(timeline);
            }*/

            double time = storyboard.Clock.Milliseconds;
            foreach (Timeline timeline in colorTimelines.Values)
            {
                timeline.BeginTime = time;
            }
            foreach (Timeline timeline in scaleTimelines.Values)
            {
                timeline.BeginTime = time;
            }
            storyboard.Clock.Resume();

            /*foreach (Timeline timeline in colorTimelines.Values)
            {
                if (storyboard.Contains(timeline)) storyboard.Remove(timeline);
                storyboard.Add(timeline);
            }
            storyboard.Clock.Resume();*/
        }
           
        void OnMouseDoubleClick(object sender, EventArgs e)
        {
            if (nodeGroup.Count > 1000)
            {
                timerAddition.Stop();
                return;

                nodeGroup.Clear();
                storyboard.Clear();
                scaleTimelines.Clear();
                colorTimelines.Clear();
            }
            Random random = new Random();

            Visual ball = new Visual(GetRandomBallMesh());
            ball.Polygonal.ClearBinormals();
            ball.Polygonal.ClearTangents();

            //ball.Properties["Color"] = new ColorProperty(new Color(random.NextDouble(), random.NextDouble(), random.NextDouble(), 1));
            ball.Technique = defaultTechnique;
            ball.Properties["Color"] = new Property<Color>(new Color(1, 0, 0, 1));
            
            ScaleTransformation scale = new ScaleTransformation();
            ball.Transformation.Add(scale);

            TranslationTransformation translation = new TranslationTransformation(new Vector3(random.NextDouble() * 10 - 5, random.NextDouble() * 10 - 5, random.NextDouble() * 10 - 5));
            ball.Transformation.Add(translation);

            Timeline timeline = new Timeline(scale.Scale);
            timeline.Keyframes.Add(new Keyframe(new Vector3(1, 1, 1), 0.0));
            timeline.Keyframes.Add(new Keyframe(new Vector3(1.5, 1.5, 1.5), 1000.0));
            timeline.Keyframes.Add(new Keyframe(new Vector3(1, 1, 1), 2000.0));

            while (scaleTimelines.ContainsKey(translation.Translation.Value)) translation.Translation.Value += Vector3.XAxis * 0.001;
            scaleTimelines.Add(translation.Translation.Value, timeline);
            storyboard.Add(timeline);

            timeline = new Timeline(ball.Properties["Color"]);
            timeline.Keyframes.Add(new Keyframe(new Color(1, 0, 0, 1), 0.0));
            timeline.Keyframes.Add(new Keyframe(new Color(0, 1, 0, 1), 1000.0));
            timeline.Keyframes.Add(new Keyframe(new Color(1, 0, 0, 1), 2000.0));

            colorTimelines.Add(translation.Translation.Value, timeline);
            storyboard.Add(timeline);

            nodeGroup.Add(ball);
        }

        void OnMouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                Point point = e.GetPosition(this);

                if (!Double.IsNaN(mouseX))
                {
                    camera.HorisontalRotation += (float)((point.X - mouseX) / 100);
                    camera.VerticalRotation += (float)((point.Y - mouseY) / 100);
                }                
                
                mouseX = point.X;
                mouseY = point.Y;
            }
            else
            {
                mouseX = Double.NaN;
                mouseY = Double.NaN;
            }
        }

        void OnSizeChanged(object sender, SizeChangedEventArgs e)
        {
            camera.Aspect = (float)(e.NewSize.Width / e.NewSize.Height);
        }

        void OnUnloaded(object sender, RoutedEventArgs e)
        {
            Loaded -= new RoutedEventHandler(OnLoaded);
            targetImage.SizeChanged -= new SizeChangedEventHandler(OnSizeChanged);
            targetImage.MouseMove -= new MouseEventHandler(OnMouseMove);
            Unloaded -= new RoutedEventHandler(OnUnloaded);
            MouseDoubleClick -= new MouseButtonEventHandler(OnMouseDoubleClick);
            MouseDown -= new MouseButtonEventHandler(OnMouseDown);
            KeyDown -= new KeyEventHandler(OnKeyDown);

            timerAddition.Stop();
            timerAnimation.Stop();
            
            //render.Shutdown();
            device.Dispose();

            targetImage.SizeChanged -= new SizeChangedEventHandler(OnSizeChanged);
            targetImage.MouseMove -= new MouseEventHandler(OnMouseMove);
        }

        void OnLoaded(object sender, RoutedEventArgs e)
        {
            targetImage.SizeChanged += new SizeChangedEventHandler(OnSizeChanged);
            targetImage.MouseMove += new MouseEventHandler(OnMouseMove);
            Unloaded += new RoutedEventHandler(OnUnloaded);
            MouseDoubleClick += new MouseButtonEventHandler(OnMouseDoubleClick);
            MouseDown += new MouseButtonEventHandler(OnMouseDown);
            KeyDown += new KeyEventHandler(OnKeyDown);

            
                        
            // Tuning the scene
            camera.HorisontalRotation = 0.84f;
            camera.VerticalRotation = 1.07f;
            camera.Target = Vector3.Zero;
            camera.Distance = 15;

            LoadVisuals();

            // Start the animations
            storyboard.Clock.Start();

            render = new Renderer(nodeGroup, camera);
            render.Invalidated += OnRenderInvalidated;// delegate { device.Invalidate(); };
            //render.Start(device);
            
            // Initialization of graphics device
            device = new WindowsPresentationDevice(targetImage, Rendering);
            device.DefaultColor = new Color(0, 0, 0, 0);
            device.Priority = DispatcherPriority.Background;
            
        }

        void OnRenderInvalidated(object sender, EventArgs args)
        {
            device.Invalidate();
        }

        void Rendering()
        {
            // TODO:
            device.Begin();
            render.Render(device);
            device.Present();
        }

        #endregion

        #region Loading geometries

        void LoadVisuals()
        {
            

        }


        Visual AddVisual(Polygonal polygonal)
        {
            Visual visual = new Visual(polygonal);
            visual.Technique = defaultTechnique;
            visual.Properties["Color"] = new Property<Color>(new Heaven.Graphics.Color(1.0, 0, 0, 1.0));
            nodeGroup.Add(visual);
            return visual;
        }

        #endregion
    }
}